//
// This file is part of cpp-lib, a library of C++ functions and classes.
// Cpp-lib is copyright (C) 2004 and onwards, The CPL Interest Group.
// 
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2.1 of the License, or (at
// your option) any later version.
// 
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
// General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with this library (see the file COPYING); if not, write to the
// Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
// 02111-1307  USA
//
// $Id$
//


#include <fstream>
#include <sstream>
#include <string>
#include <exception>
#include <stdexcept>

#include <cerrno>
#include <cstring>
#include <cassert>

#include "cpp-lib/util.h"
#include "cpp-lib/file-util.h"
#include "cpp-lib/platform/wrappers.h"

using namespace cpl::util::file ;
using namespace cpl::detail_ ;


std::auto_ptr< std::filebuf > cpl::util::file::open_readbuf( 
  std::string const& file                ,  
  std::string      & which               , 
  std::vector< std::string > const& path 
) {

  std::auto_ptr< std::filebuf > ret( new std::filebuf() ) ;
  
  std::string tried ;

  for( unsigned long i = 0 ; i < path.size() ; ++i ) {

    std::string const pathname = path[ i ] + "/" + file ;

    ret->open
	  ( pathname.c_str() , std::ios_base::in | std::ios_base::binary ) ;

    if( !ret->is_open() ) 
    { tried += "\n" + pathname + ": " + std::strerror( errno ) ; }
    else { which = pathname ; return ret ; }

  }

  ret->open
	( file.c_str() , std::ios_base::in | std::ios_base::binary ) ;
    
  if( !ret->is_open() ) 
  { tried += "\n" + file + ": " + std::strerror( errno ) ; }
  else { which = file ; return ret ; }

  assert( !ret->is_open() ) ;

  throw std::runtime_error( 
    "couldn't open " + file + " for reading:" + tried
  ) ;

}


std::auto_ptr< std::istream > cpl::util::file::open_read( 
  std::string const& file                ,  
  std::string      & which               , 
  std::vector< std::string > const& path 
) {

  std::auto_ptr< std::streambuf > buf ;
  buf.reset( open_readbuf( file , which , path ).release() ) ;

  return std::auto_ptr< std::istream >( new owning_istream( buf ) ) ;

}


std::auto_ptr< std::filebuf > 
cpl::util::file::open_writebuf( std::string const& file ) {

  std::auto_ptr< std::filebuf > ret( new std::filebuf() ) ;
 
  ret->open( file.c_str() , std::ios_base::out | std::ios_base::binary ) ;

  if( !ret->is_open() ) {

    throw std::runtime_error(
      "couldn't open " + file + " for writing: " + std::strerror( errno )
    ) ;

  }

  return ret ;

}


std::auto_ptr< std::ostream > 
cpl::util::file::open_write( std::string const& file ) {
  
  std::auto_ptr< std::streambuf > buf ;
  buf.reset( open_writebuf( file ).release() ) ;

  return std::auto_ptr< std::ostream >( new owning_ostream( buf ) ) ;

}


std::string cpl::util::file::basename(
  std::string const& name ,
  std::string const& suffix
) {

  if(
       name.size() >= suffix.size()
    && std::equal( name.end() - suffix.size() , name.end() , suffix.begin() )
  )
  { return std::string( name.begin() , name.end() - suffix.size() ) ; }
  else
  { return name ; }

}


cpl::util::file::File::File( std::string const& name ) 
: impl( new file_impl( name ) ) {}

double const cpl::util::file::File::modification_time() 
{ return impl->modification_time() ; }


bool cpl::util::file::file_name_watcher::modified() {

  double const tt = File( name ).modification_time() ;

  if( tt > t ) { t = tt ; return true ; }
  else         { return false ; }

}


bool cpl::util::file::file_watcher::modified() {

  double const tt = f.modification_time() ;

  if( tt > t ) { t = tt ; return true ; }
  else         { return false ; }

}
